package love.bot.forli.group.review.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import love.bot.forli.group.review.bean.Answer;
import love.bot.forli.group.review.bean.Dto.*;
import love.bot.forli.group.review.bean.Option;
import love.bot.forli.group.review.bean.Result;
import love.bot.forli.group.review.bean.Vo.CreateVo;
import love.bot.forli.group.review.bean.Vo.SubmitVo;
import love.bot.forli.group.review.entity.*;
import love.bot.forli.group.review.mapper.*;
import love.bot.forli.group.review.service.TestService;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

import static love.bot.forli.group.review.util.Constants.KNOWN_QUESTION;
import static love.bot.forli.group.review.util.Constants.PASS_SCORE;

/**
 * @author Kontori
 */
@Service
public class TestServiceImpl extends ServiceImpl<TestMapper, Test> implements TestService {

    private TestMapper testMapper;

    @Autowired
    public void setTestMapper(TestMapper testMapper) {
        this.testMapper = testMapper;
    }

    private QuestionMapper questionMapper;

    @Autowired
    public void setQuestionMapper(QuestionMapper questionMapper) {
        this.questionMapper = questionMapper;
    }

    private TestListMapper testListMapper;

    @Autowired
    public void setTestListMapper(TestListMapper testListMapper) {
        this.testListMapper = testListMapper;
    }

    private CodeMapper codeMapper;

    @Autowired
    public void setCodeMapper(CodeMapper codeMapper) {
        this.codeMapper = codeMapper;
    }

    private QuestionTypeMapper questionTypeMapper;

    @Autowired
    public void setQuestionTypeMapper(QuestionTypeMapper questionTypeMapper) {
        this.questionTypeMapper = questionTypeMapper;
    }

    private QuestionGroupMapper questionGroupMapper;

    @Autowired
    public void setQuestionGroupMapper(QuestionGroupMapper questionGroupMapper) {
        this.questionGroupMapper = questionGroupMapper;
    }

    private QuestionOptionMapper questionOptionMapper;

    @Autowired
    public void setQuestionOptionMapper(QuestionOptionMapper questionOptionMapper) {
        this.questionOptionMapper = questionOptionMapper;
    }

    private QuestionDto fillQuestionDto(Integer questionId) {
        Question question = questionMapper.selectById(questionId);
        QuestionDto questionDto = new QuestionDto();
        BeanUtils.copyProperties(question, questionDto);
        questionDto.setQuestionGroupName(questionGroupMapper.selectById(question.getQuestionGroupId()).getGroupName());
        List<Option> options = new ArrayList<>();
        questionOptionMapper.selectList(new QueryWrapper<QuestionOption>()
                .eq("question_id", question.getQuestionId())
                .orderByAsc("`order`")).forEach(questionOption -> {
            Option option = new Option(questionOption.getContent(), questionOption.getOrder());
            options.add(option);
        });
        questionDto.setOptions(options);
        questionDto.setQuestionGroupType(questionTypeMapper.selectById(
                questionGroupMapper.selectById(question.getQuestionGroupId())
                        .getQuestionTypeId()).getTypeName());
        return questionDto;
    }

    @Override
    public Result<Object> createTest(CreateVo createVo) {
        List<Integer> questionIds = new ArrayList<>();
        //注入试题
        AtomicReference<Integer> maxScore = new AtomicReference<>(0);
        questionTypeMapper.selectList(null).forEach(questionType -> {
            List<Question> questionList = questionMapper.selectList(new QueryWrapper<Question>()
                    .inSql("question_group_id",
                            "select question_group_id from question_group where question_type_id = "
                                    + questionType.getQuestionTypeId()));
            Collections.shuffle(questionList);
            questionList.subList(0, questionType.getNeedCount()).forEach(question -> {
                maxScore.updateAndGet(v -> v + question.getScore());
                questionIds.add(question.getQuestionId());
            });
        });
        //插入试题记录
        Test test = new Test();
        test.setTesterQq(createVo.getTesterQq());
        test.setInviterQq(createVo.getInviterQq());
        test.setMaxScore(maxScore.get());
        test.setPassScore(PASS_SCORE);
        testMapper.insert(test);
        //插入题目列表
        questionIds.forEach(questionId -> {
            TestList testList = new TestList();
            testList.setTestId(test.getTestId());
            testList.setQuestionId(questionId);
            testListMapper.insert(testList);
        });
        return Result.success("创建成功", test.getTestId());
    }

    @Override
    public Result<TestDto> getTest(Integer testId) {
        Test test = testMapper.selectById(testId);
        if (test == null) {
            return Result.fail("试题卷不存在");
        }
        if (test.getScoreTotal() != null) {
            return Result.fail("测试已结束");
        }
        List<QuestionDto> questionDtoList = new ArrayList<>();
        testListMapper.selectList(new QueryWrapper<TestList>().eq("test_id", testId)).forEach(testList -> {
            QuestionDto questionDto = fillQuestionDto(testList.getQuestionId());
            questionDtoList.add(questionDto);
        });
        return Result.success(
                new TestDto(testId, questionDtoList.size(), test.getMaxScore(), test.getPassScore(), questionDtoList));
    }

    @Override
    public Result<ResultDto> submitTest(SubmitVo submitVo) {
        Integer testId = submitVo.getTestId();
        Test test = testMapper.selectById(testId);
        if (test == null) {
            return Result.fail("对应测试卷不存在");
        }
        AtomicReference<Boolean> knownCheck = new AtomicReference<>(false);
        Integer finalScoreTotal = calculateScore(submitVo.getAnswers(), testId, knownCheck);
        test.setScoreTotal(finalScoreTotal);
        if (knownCheck.get()) {
            test.setPass(0);
            testMapper.updateById(test);
            return Result.success("常识题有答错", new ResultDto(false, finalScoreTotal, null));
        }
        if (finalScoreTotal >= test.getPassScore()) {
            test.setPass(1);
            testMapper.updateById(test);
            Code preCode = codeMapper.selectOne(Wrappers.lambdaQuery(Code.class)
                    .eq(Code::getQq, test.getTesterQq())
                    .eq(Code::getStatus, 0));
            if (preCode != null) {
                preCode.setStatus(2);
                codeMapper.updateById(preCode);
            }
            String codeValue = RandomStringUtils.randomAlphanumeric(10);
            codeMapper.insert(Code.builder()
                    .testId(testId)
                    .qq(test.getTesterQq())
                    .value(codeValue)
                    .inviterQq(test.getInviterQq())
                    .status(0).build());
            return Result.success("提交成功", new ResultDto(true, finalScoreTotal, codeValue));
        } else {
            test.setPass(0);
            testMapper.updateById(test);
            return Result.success("未及格", new ResultDto(false, finalScoreTotal, null));
        }
    }

    private Integer calculateScore(List<Answer> answers, Integer testId, AtomicReference<Boolean> knownCheck) {
        AtomicReference<Integer> scoreTotal = new AtomicReference<>(0);
        answers.forEach(answer -> {
            Question question = questionMapper.selectById(answer.getQuestionId());
            Integer score = 0;
            TestList testList = testListMapper.selectOne(new QueryWrapper<TestList>()
                    .eq("question_id", question.getQuestionId())
                    .eq("test_id", testId));
            String userAnswer = "";
            if (answer.getUserAnswer().size() != 0) {
                Collections.sort(answer.getUserAnswer());
                userAnswer = String.join(",", answer.getUserAnswer());
                switch (question.getType()) {
                    case 0:
                    case 2:
                        //单选题或判断题
                        if (Objects.equals(question.getAnswer(), answer.getUserAnswer().get(0))) {
                            score = question.getScore();
                        }
                        break;
                    case 1:
                        //多选题
                        List<String> trueList = Arrays.asList(question.getAnswer().split(","));
                        List<String> userList = answer.getUserAnswer();
                        if (userList.size() <= trueList.size()) {
                            if (trueList.containsAll(userList)) {
                                if (userList.size() == trueList.size()) {
                                    score = question.getScore();
                                } else {
                                    score = question.getScore() / 2;
                                }
                            }
                        }
                        break;
                    default:
                        break;
                }
            }
            if (questionGroupMapper.selectOne(new QueryWrapper<QuestionGroup>()
                            .eq("question_group_id", question.getQuestionGroupId()))
                    .getQuestionTypeId() == KNOWN_QUESTION && !score.equals(question.getScore())) {
                knownCheck.set(true);
            }
            testList.setUserAnswer(userAnswer);
            testList.setScore(score);
            testListMapper.updateById(testList);
            Integer finalScore = score;
            scoreTotal.updateAndGet(v -> v + finalScore);
        });
        return scoreTotal.get();
    }

    @Override
    public Result<TestViewDto> view(Integer testId) {
        Test test = testMapper.selectById(testId);
        if (test == null) {
            return Result.fail("试题卷不存在");
        }
        TestViewDto testViewDto = new TestViewDto();
        BeanUtils.copyProperties(test, testViewDto);
        List<QuestionAnswerDto> questionAnswerList = new ArrayList<>();
        testListMapper.selectList(new QueryWrapper<TestList>().eq("test_id", testId)).forEach(testList -> {
            QuestionDto questionDto = fillQuestionDto(testList.getQuestionId());
            QuestionAnswerDto questionAnswerDto = new QuestionAnswerDto();
            if (testList.getUserAnswer() != null) {
                questionAnswerDto.setUserAnswer(Arrays.asList(testList.getUserAnswer().split(",")));
            } else {
                questionAnswerDto.setUserAnswer(Collections.emptyList());
            }
            questionAnswerDto.setUserScore(testList.getScore());
            BeanUtils.copyProperties(questionDto, questionAnswerDto);
            questionAnswerList.add(questionAnswerDto);
        });
        testViewDto.setQuestionAnswerList(questionAnswerList);
        testViewDto.setQuestionTotal(questionAnswerList.size());
        return Result.success(testViewDto);
    }

    @Override
    public Result<String> delete(Integer testId) {
        Test test = testMapper.selectById(testId);
        if (test == null) {
            return Result.fail("试题不存在");
        }
        Code code = codeMapper.selectOne(new QueryWrapper<Code>().eq("test_id", testId));
        if (code != null) {
            code.setTestId(0);
            codeMapper.updateById(code);
        }
        testListMapper.delete(new QueryWrapper<TestList>().eq("test_id", testId));
        testMapper.deleteById(test);
        return Result.success("删除成功", null);
    }
}
